<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .background-processing-container {
            height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            gap: 50px;


        }

        .source {
            display: flex;
            justify-content: space-around;
            gap: 50px;
            align-items: center;
            text-align: center;
        }


        video,
        canvas {
            width: 480px;
            height: 300px;
            border: 4px solid #374685;
        }
    </style>
</head>

<body>
    <!-- 
        主要原理是通过 canvas 将视频中的每一帧画到画布上，然后将画布中的像素逐个与设定的背景色（默认是绿色，你可以更换为任意符合你背景的颜色）进行计算，比较后的差值达到设定的阈值时，对其进行处理，将其更换为预先准备好的背景图的图像数据，最后将处理后的图像数据再画到虚拟背景画布上，通过虚拟背景画布拿到媒体流后给到 video 标签播放， 这样就实现了视频的虚拟背景效果。
        下面我们来看看具体的实现。
        为保持大小一致，这里我们统一设置画布和视频的宽高为 480*300
    -->
    <div class="background-processing-container">
        <canvas id="backgroundImg" width="480" height="300" class="background-img"></canvas>

        <video id="real-video" width="480" height="300" autoplay muted></video>
        <video id="virtual-video" width="480" height="300" autoplay muted></video>

        <div class="control">
            你的背景色：
            <input id="color" type="color" />
            容差值：
            <!-- <el-input-number v-model="allowance" :step="1" step-strictly /> -->
            <!-- <el-slider v-model="allowance" :max="300" :step="1" /> -->
        </div>
    </div>


    <script>
        // import backgroundImg from '@/assets/background2.png'

        const WIDTH = 480
        const HEIGHT = 300

        // 原本的视频
        let realVideo
        let realVideoCanvas
        let realVideoCtx
        let realVideoImageData

        // 虚拟的视频
        let virtualVideo
        let virtualVideoCanvas
        let virtualVideoCtx

        document.querySelector('#color').onchange = function (e) {
            // console.log(hexToRgb(e.target.value))
            backgroundColor = hexToRgb(e.target.value) ? hexToRgb(e.target.value) : "#000000"
        }

        // 重要：第一步，获取背景图的信息
        let backgroundImageData
        // 获取背景图像数据
        function getBackgroundImageData() {
            return new Promise((resolve) => {
                const backgroundCanvas = document.querySelector('#backgroundImg')
                const backgroundCtx = backgroundCanvas.getContext('2d')
                const img = new Image()
                // img.src = backgroundImg
                img.src = 'background.png'
                img.setAttribute('crossOrigin', '')
                img.onload = () => {
                    backgroundCtx.drawImage(
                        img,
                        0,
                        0,
                        backgroundCanvas.width,
                        backgroundCanvas.height,
                    )
                    //  用于合成事件
                    backgroundImageData = backgroundCtx.getImageData(
                        0,
                        0,
                        backgroundCanvas.width,
                        backgroundCanvas.height,
                    )
                    resolve(0)
                }
            })
        }


        // 合成视频
        function drawVideoToCanvas(realVideo) {
            // realVideo 是 设想
            // 摄像头的canvas
            realVideoCanvas = document.createElement('canvas')
            realVideoCtx = realVideoCanvas.getContext('2d')

            virtualVideoCanvas = document.createElement('canvas')
            virtualVideoCtx = virtualVideoCanvas.getContext('2d')

            realVideoCanvas.width = virtualVideoCanvas.width = WIDTH
            realVideoCanvas.height = virtualVideoCanvas.height = HEIGHT

            // 每隔 100ms 将真实的视频写到 canvas 中，并获取视频的图像数据
            setInterval(() => {
                // 下面的人物
                realVideoCtx.drawImage(
                    realVideo,
                    0,
                    0,
                    realVideoCanvas.width,
                    realVideoCanvas.height,
                )
                // 渲染图片
                realVideoImageData = realVideoCtx.getImageData(
                    0,
                    0,
                    realVideoCanvas.width,
                    realVideoCanvas.height,
                )
                // 处理真实视频的图像数据，将其写到虚拟视频的 canvas 中
                processFrameDrawToVirtualVideo()
            }, 50)
            // 从 VirtualVideoCanvas 中获取视频流并在 virtualVideo 中播放
            virtualVideo = document.querySelector('#virtual-video')
            const stream = virtualVideoCanvas.captureStream(30)
            // 重要，从这里canvas 变成最终的流
            virtualVideo.srcObject = stream
        }

   
        // ！！！重要：合成：处理真实视频的图像数据，将其写到虚拟视频的 canvas 中
        function processFrameDrawToVirtualVideo() {
            // 逐像素计算与要处理的目标颜色的差值，如果差值小于阈值，则将该像素设置为背景图片中的对应像素
            for (let i = 0; i < realVideoImageData.data.length; i += 4) {
                const r = realVideoImageData.data[i]
                const g = realVideoImageData.data[i + 1]
                const b = realVideoImageData.data[i + 2]
                const a = realVideoImageData.data[i + 3]
                const bgR = backgroundImageData.data[i]
                const bgG = backgroundImageData.data[i + 1]
                const bgB = backgroundImageData.data[i + 2]
                const bgA = backgroundImageData.data[i + 3]

                // 计算与背景色的差值
                const diff = colorDiff([r, g, b], backgroundColor)
                // 当差值小于设定的阈值，则将该像素设置为背景图片中的对应像素
                if (diff < allowance) {
                    realVideoImageData.data[i] = bgR
                    realVideoImageData.data[i + 1] = bgG
                    realVideoImageData.data[i + 2] = bgB
                    realVideoImageData.data[i + 3] = bgA
                }
            }
            // 将处理后的图像数据写到虚拟视频的 canvas 中
            virtualVideoCtx.putImageData(realVideoImageData, 0, 0)
        }

        // 计算颜色差异
        function colorDiff(rgba1, rgba2) {
            let d = 0
            for (let i = 0; i < rgba1.length; i++) {
                d += (rgba1[i] - rgba2[i]) ** 2
            }
            return Math.sqrt(d)
        }

        // 十六进制转 rgb
        function hexToRgb(hex) {
            const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
            console.log("hexToRgb:" + hex)
            return result
                ? [
                    parseInt(result[1], 16),
                    parseInt(result[2], 16),
                    parseInt(result[3], 16),
                ]
                : null
        }
        // 初始的背景色
        let color = '#000000'
        // let temp = rgba(100,200,100)
        // setTimeout(()=>{
        //     color ='#00000'
        // },1000)
        // 重要：设置 diff 阈值
        const allowance = 162
        let backgroundColor

        // 重要：需要扣除的背景色
        backgroundColor = hexToRgb(color)
        // watch(
        //     () => color.value,
        //     (newVal) => {
        //         // 十六进制转 rgb
        //         backgroundColor = hexToRgb(newVal)
        //     },
        //     {
        //         immediate: true,
        //     },
        // )
        // 开始
        async function start() {
            // 重要第一步：在canvas绘制图像，显示出来
            await getBackgroundImageData()
            // 重要第二步：显示出来没有经过变化的原始摄像头，其实是没有什么意义的
            const stream = await navigator.mediaDevices.getUserMedia(
                {
                    video: {
                        width: WIDTH,
                        height: HEIGHT,
                    }
                })
            realVideo = document.querySelector('#real-video')
            realVideo.srcObject = stream

            // 重要第三步：这个是主要逻辑方法
            drawVideoToCanvas(realVideo)
        }
        start()
        // start()

    </script>

</body>

</html>